在比赛的时候有点猪脑过载,我就是一点点人工分析过来的,首先考虑 flag 的格式,”ictf{“.hex() == ‘696374667b’,然后我们在密文中找第0,2,6,7个字符相同,第4,8个字符相同的子串,会发现刚好只有一个,于是我们就能确定下来一些字符。然后根据已知的碎片信息一点点分析,在解题代码的注释中我列出了字符替换的逻辑。
import textwrap from binascii import crc32 from Crypto.Util.number import getPrime
p, q = getPrime(1024), getPrime(1024) n = p*q e = 65537 d = pow(e, -1, (p-1)*(q-1))
PASSWORD = b"give me the flag!!!"
print("--------------------------------------------------------------") print(" Welcome to the secure signing app! ") print(" I will sign whatever you want, except the secret password. ") print("--------------------------------------------------------------") print() print("--------------------------------------------------------------") print("\n".join(textwrap.wrap(f"{n = }", len("-------------------------------------------------------------")))) print("--------------------------------------------------------------") print()
whileTrue: print("1. Sign") print("2. Get flag") choice = int(input())
if choice == 1: print("Enter message:") message = input().encode() # crc32 is secure and has no collisions, but just in case if message == PASSWORD or crc32(message) == crc32(PASSWORD): print("Stop this trickery!") exit() print("Signature:", pow(crc32(message), d, n)) elif choice == 2: print("Enter the signature for the password:") s = int(input()) if pow(s, e, n) == crc32(PASSWORD): print("You win! The flag is", open("flag.txt").read()) exit() else: print("Wrong.") exit()
题目基于 RSA 签名系统,需要我们给出 PASSWORD = b"give me the flag!!!" 的签名,另外这里的签名不是对消息进行哈希,而是使用 crc32 来表示。我们知道 crc32 只有 4 个字节,因此这样一个多对一的映射在明文输入大于4个字节的时候是非常好碰撞的。所以这里首先计算 crc32(PASSWORD) 得到值 3542523789,不过由于限制
1 2 3
if message == PASSWORD or crc32(message) == crc32(PASSWORD): print("Stop this trickery!") exit()
c = -0.7578486465144361653056740883647981074157721568235263947812770328593706155446273431983003083023944193451634501133844062222318380912228469321984711771640337084400211818130699382144693337133198331117688092846455855532799303682791981067718891947573941091671581719597626862194794682042719495503282817868258547714 ac = atan(c)
from Crypto.Util.number import * from math import gcd
defkeygen(sz): p = getPrime(sz // 2) q = getPrime(sz // 2) n = p * q phi = (p - 1) * (q - 1) e_fast = getPrime(sz // 2) # to make encryption more time consuming :P e_slow = e_fast + getPrime(sz // 3) * phi d = pow(e_fast, -1, phi) return (n, e_slow), (n, e_fast), (n, d)
defencrypt(pub, m): n, e = pub return pow(m, e, n)
defdecrypt(priv, c): n, d = priv return pow(c, d, n)
pub, pub_fast, priv = keygen(2048) m = bytes_to_long(open("flag.txt", "rb").read().strip()) c = encrypt(pub, m) assert c == encrypt(pub_fast, m) assert decrypt(priv, c) == m print(f"{pub = }") print(f"{c = }")
print(f'You guessed {total} cards! My hand was:', *[DECK[c] for c in cards]) money += 2*(total - 5)*bet elif choice == 'exit': print("Chickened out, huh? No flag for you.") exit()
print("Woops... Looks like you guessed your way out of money :>")
from operator import lshift, rshift from tqdm import trange from random import getrandbits from galois import GF import numpy as np
DECK = "🂡🂢🂣🂤🂥🂦🂧🂨🂩🂪🂫🂭🂮" F = GF(13) # lucky number n = 10
# Let's not use a singular matrix, please. # We do quality random over here. global M M = [[0]] while np.linalg.det(M) == 0: M = F.Random((n, n))
global money money = 15000 global cards cards = F.Random(n) while all(int(c) == 0for c in cards): cards = F.Random(n)
deflocal_game(choice,bet,inputs): global money global cards while money > 0: #print('balance:', money)
if choice == 'buy flag': if money < 1_000_000_000: print("You're too poor!") continue
from redacted import FLAG money -= 1_000_000_000 print("What a guess god! Here's your flag:", FLAG)
elif choice == 'play': assert money >= bet > 0 #print("Can you blindly guess my cards?") cards = np.linalg.matrix_power(M, getrandbits(32)) @ cards # shuffle cards guess = M @ F([*map(DECK.index, inputs.split())]) # blind guess total = sum(cards == guess)
#print(f'You guessed {total} cards! My hand was:', *[DECK[c] for c in cards]) money += 2*(total - 5)*bet return [int(c) for c in cards],total elif choice == 'exit': print("Chickened out, huh? No flag for you.") exit()
#print("Woops... Looks like you guessed your way out of money :>")
n = 10
hands = [] col = [set(range(13)) for _ in range(n)] MM = [] i = 0 while i < n: guesscard = i*[0] + [1] + (n-i-1)*[0] hand, total = local_game('play',1, ' '.join(DECK[int(c)] for c in guesscard)) hands.append(hand) if total == 0: for r, c in zip(col, hand): r.discard(c)
if all(len(r) == 1for r in col): MM.append([c for r in col for c in r]) col = [set(range(13)) for _ in range(n)] i += 1 print('i =', i)
# https://github.com/jvdsn/crypto-attacks/blob/master/shared/matrices.py defdlog_equation(A, x, y): """ Computes l such that A^l * x = y, in GF(p). :param A: the matrix A :param x: the vector x :param y: the vector y :return: l, or None if l could not be found """ assert A.is_square()
# TODO: extend to GF(p^k) if necessary? J, P = A.jordan_form(transformation=True) x = P ** -1 * x y = P ** -1 * y r = 0 for s1, s2 in zip(*J.subdivisions()): S = J.subdivision(s1, s2) assert S.is_square()
n = S.nrows() r += n if n >= 2: x1 = x[r - 1] x2 = x[r] y1 = y[r - 1] y2 = y[r] l = S[n - 1, n - 1] * (y1 - x1 * y2 / x2) / y2 return int(l)